package iotwisp.base.util;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.LazyInitializationException;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.proxy.HibernateProxy;
import org.springframework.util.Assert;

import javax.persistence.Id;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;

public class BeanUtils {

	/**
	 * 获取有@Id注解的Entity属性
	 *
	 * @param clazz
	 * @return
	 */
	public static Field getEntityIdField(Class<?> clazz) {
		Class<?> currentClazz = clazz;
		while (currentClazz != Object.class) {
			try {
				Field[] fields = currentClazz.getDeclaredFields();
				for (Field field : fields) {
					if (field.getAnnotation(Id.class) != null) {
						return field;
					}
				}
			} catch (Exception e) {
			}
			currentClazz = currentClazz.getSuperclass();
		}
		return null;
	}

	/**
	 * 创建延迟加载失败时应返回的对象
	 *
	 * @param obj
	 * @return
	 */
	public static Object createInitializeFailedObject(Object obj) {
		// 根据没获取到的代理对象类型来决定返回什么
		if (obj instanceof HibernateProxy) {
			// 如果是个对象
			HibernateProxy proxy = (HibernateProxy) obj;
			Serializable id = proxy.getHibernateLazyInitializer().getIdentifier();
			if (id == null) {
				// 如果代理对象没有主键直接返回null
				return null;
			}
			try {
				Class<?> entityClass = proxy.getHibernateLazyInitializer().getPersistentClass();
				// 获取主键字段
				Field idField = getEntityIdField(entityClass);
				if (idField != null) {
					// 创建一个空对象，把主键字段的值set进去，返回这个只有id的对象
					Object entity = entityClass.newInstance();
					idField.setAccessible(true);
					idField.set(entity, id);
					return entity;
				}
			} catch (Exception e1) {
			}
			return null;
		} else if (obj instanceof PersistentCollection) {
			// 如果是个集合，创建空集合返回
			if (obj instanceof List) {
				return new ArrayList<Object>();
			} else if (obj instanceof Map) {
				return new HashMap<Object, Object>();
			} else if (obj instanceof Set) {
				return new HashSet<Object>();
			}
			return null;
		}
		return obj;
	}

	/**
	 * 获取属性，遇到延迟加载异常返回NULL
	 *
	 * @param obj
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getProperty(Object obj, Class<T> clazz, boolean fetch) {
		if (obj == null || clazz == null) {
			return (T) obj;
		}

//		if (obj instanceof HibernateProxy) {
//			System.out.println("##########################");
//		}

//        System.out.println("xxx:" + clazz);
		// 获取字段值时如果遇到代理，按参数决定要不要发出查询加载
		if (obj instanceof HibernateProxy) {
			T entity = fetch(obj, fetch);
			return entity;
//            if (!Hibernate.isInitialized(obj)) {
//                if (fetch) {
//                    T entity = fetch(obj);
//                    return entity;
//                } else {
//                    return null;
//                }
//            }
		}

		if (obj instanceof PersistentCollection) {
			if (!Hibernate.isInitialized(obj)) {
				T entity = fetch(obj, fetch);
				return entity;
			}
		}
		return (T) obj;
	}

	public static <T> T fetch(Object obj, boolean tryInit) {
		try {
			if (tryInit) {
				Hibernate.initialize(obj);
				return (T) initializeAndUnproxy(obj);
			} else {
				return (T) createInitializeFailedObject(obj);
			}
		} catch (LazyInitializationException e) {
			// session已关闭，查不了数据库
			return (T) createInitializeFailedObject(obj);
		} catch (ObjectNotFoundException e) {
			return (T) createInitializeFailedObject(obj);
		} catch (HibernateException e) {
			return null;
		}
	}

	public static <T> T initializeAndUnproxy(T entity) {
		if (entity == null) {
			throw new NullPointerException("Entity passed for initialization is null");
		}

		Hibernate.initialize(entity);
		if (entity instanceof HibernateProxy) {
			entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
		}
		return entity;
	}

	/**
	 * 直接读取对象属性值,无视private/protected修饰符,不经过getter函数.
	 */
	public static Object getFieldValue(final Object object, final String fieldName) {
		Field field = getDeclaredField(object, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}

		makeAccessible(field);

		Object result = null;
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("never happend exception!", e);
		}
		return result;
	}

	/**
	 * 直接设置对象属性值,无视private/protected修饰符,不经过setter函数.
	 */
	public static void setFieldValue(final Object object, final String fieldName, final Object value) {
		Field field = getDeclaredField(object, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}

		makeAccessible(field);

		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("never happend exception!", e);
		}
	}

	/**
	 * 循环向上转型,获取对象的DeclaredField.
	 */
	protected static Field getDeclaredField(final Object object, final String fieldName) {
		Assert.notNull(object);
		return getDeclaredField(object.getClass(), fieldName);
	}

	/**
	 * 循环向上转型,获取类的DeclaredField.
	 */
	@SuppressWarnings("rawtypes")
	protected static Field getDeclaredField(final Class clazz, final String fieldName) {
		Assert.notNull(clazz);
		Assert.hasText(fieldName);
		for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(fieldName);
			} catch (NoSuchFieldException e) {
				// Field不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	/**
	 * 强制转换fileld可访问.
	 */
	protected static void makeAccessible(final Field field) {
		if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
			field.setAccessible(true);
		}
	}

	public static Object getSimpleProperty(Object bean, String propName) throws IllegalArgumentException,
			SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Object returnValue = bean;
		if (StringUtils.contains(propName, ".")) {
			String[] propNames = StringUtils.split(propName, ".");
			for (int i = 0; i < propNames.length; i++) {

				returnValue = returnValue.getClass().getMethod(getReadMethod(propNames[i])).invoke(returnValue);
			}
			return returnValue;
		} else {
			return bean.getClass().getMethod(getReadMethod(propName)).invoke(bean);
		}

	}

	public static void setSimpleProperty(Object bean, String propName, Object value) throws IllegalArgumentException,
			SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if (StringUtils.contains(propName, ".")) {
			String[] propNames = StringUtils.split(propName, ".");
			for (int i = 0; i < propNames.length; i++) {

				if (i < propNames.length - 1) {
					bean = bean.getClass().getMethod(getReadMethod(propNames[i])).invoke(bean);
				} else {
					bean.getClass().getMethod(getWriteMethod(propNames[i]), value.getClass()).invoke(bean, value);
				}

			}
		} else {
			bean.getClass().getMethod(getWriteMethod(propName), value.getClass()).invoke(bean, value);
		}

	}

	private static String getReadMethod(String name) {
		return "get" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
	}

	private static String getWriteMethod(String name) {
		return "set" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
	}
}
